package in.co.codedoc.vsh;

import in.co.codedoc.json.JSONArrayValue;
import in.co.codedoc.json.JSONObjectValue;
import in.co.codedoc.json.JSONValue;

import java.io.IOException;
import java.io.Writer;

import org.apache.velocity.context.InternalContextAdapter;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.TemplateInitException;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.directive.Directive;
import org.apache.velocity.runtime.parser.node.ASTReference;
import org.apache.velocity.runtime.parser.node.Node;
import org.apache.velocity.runtime.parser.node.SimpleNode;

public class JSONForEach extends Directive
{
	@Override
	public String getName()
	{
		return "JSONForEach";
	}

	@Override
	public int getType()
	{
		return BLOCK;
	}
	

	@Override
	public void init(RuntimeServices rs, InternalContextAdapter context,
			Node node) throws TemplateInitException
	{
		if(node.jjtGetNumChildren() != 4)
		{
			throw new RuntimeException(usage);
		}
		
        super.init(rs, context, node);

        counterName = rsvc.getString(RuntimeConstants.COUNTER_NAME);
        counterInitialValue = rsvc.getInt(RuntimeConstants.COUNTER_INITIAL_VALUE);
        maxNbrLoops = rsvc.getInt(RuntimeConstants.MAX_NUMBER_LOOPS,Integer.MAX_VALUE);
        
        if (maxNbrLoops < 1)
        {
            maxNbrLoops = Integer.MAX_VALUE;
        }
        
        SimpleNode sn = (SimpleNode) node.jjtGetChild(0);

        if (sn instanceof ASTReference)
        {
            itemName = ((ASTReference) sn).getRootString();
            keyName = itemName + "Key";
        }
        else
        {
			throw new RuntimeException(usage);
        }
        
	}

	@Override
	public boolean render(final InternalContextAdapter context, final Writer writer,final Node node) 
		throws IOException, ResourceNotFoundException,ParseErrorException, 
			MethodInvocationException
	{
        Object collection = node.jjtGetChild(2).value(context);

        if (collection != null)
        {
        	String[] interestingNames = new String[]{counterName,itemName,keyName};
        	Object[] interestingValues = new Object[interestingNames.length];
        	
        	for(int i = 0; i< interestingNames.length;++i)
        	{
        		interestingValues[i] = context.get(interestingNames[i]); 
        	}

        	try
        	{
        		context.put(counterName, counterInitialValue);
        		
	        	if(collection instanceof JSONObjectValue)
	        	{
	        		JSONObjectValue jsonObject = (JSONObjectValue)collection;
	        		
	        		jsonObject.ForEach
	        		(
	        			new JSONObjectValue.ForEach<Object>()
	        			{
							@Override
							public void Do(int index, JSONValue key,JSONValue value)
							{
								try
								{
									context.put(counterName, index + counterInitialValue);
									context.put(keyName, key);
									context.put(itemName, value);
									node.jjtGetChild(3).render(context, writer);
								}
								catch(Throwable th)
								{
									Logger.logger.error
									(
										"Exception while looping on (" +
										index  + "," + key.Encode() + "," + value.Encode() +
										") at line " + getLine() + " of " + context.getCurrentTemplateName()
									);
								}
							}
	        			}
	        		);
	        	}
	        	else if(collection instanceof JSONArrayValue)
	        	{
	        		JSONArrayValue array = (JSONArrayValue)collection;
	        		
	        		array.ForEach
	        		(
	        			new JSONArrayValue.ForEach<Object>()
	        			{
							@Override
							public void Do(int index, JSONValue value)
							{
								try
								{
									context.put(counterName, index + counterInitialValue);
									context.put(keyName, index);
									context.put(itemName, value);
									node.jjtGetChild(3).render(context, writer);
								}
								catch(Throwable th)
								{
									Logger.logger.error
									(
										"Exception while looping on (" +
										index  + "," + value.Encode() +
										") at line " + getLine() + " of " + context.getCurrentTemplateName()
									);
								}
							}
	        			}
	        		);
	        	}
	        	else
	        	{
	        		try
	        		{
						context.put(counterName,counterInitialValue);
						context.put(keyName,0);
						context.put(itemName,collection);
						node.jjtGetChild(3).render(context, writer);
					}
					catch(Throwable th)
					{
						Logger.logger.error
						(
							"Exception while looping on sole element " +
							collection +
							" at line " + getLine() + " of " + context.getCurrentTemplateName()
						);
					}
	        	}
        	}
        	finally
        	{
            	for(int i = 0; i< interestingNames.length;++i)
            	{
            		if(interestingValues[i] != null)
            		{
            			context.put(interestingNames[i],interestingValues[i]);            			
            		}
            		else
            		{
            			context.remove(interestingNames[i]);
            		}
            	}
        	}
        }

		return true;
	}

	private String counterName;
	private int counterInitialValue;
	
	private String itemName;
	private String keyName;

	private int maxNbrLoops;
	private String usage = "JSONForEach should be used as #JSONForEach($foo in $bar.blah())\n...\n#end";
}
